package com.xuzimian.globaldemo.spring.security.oauth2.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;

/**
 * @description: OAuth2 授权服务器配置
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig implements AuthorizationServerConfigurer {


    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置授权服务器端点的安全
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                //允许表单认证
                .allowFormAuthenticationForClients()
                //设置客户端访问/oauth/token 获取token时，密码(clientSecret)未加密导致的错误，设置
                .passwordEncoder(NoOpPasswordEncoder.getInstance());

//        security.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
//                "hasAuthority('ROLE_TRUSTED_CLIENT')")
//                //允许表单认证
//                .allowFormAuthenticationForClients()
//                //设置客户端访问/oauth/token 获取token时，密码(clientSecret)未加密导致的错误，设置
//                .passwordEncoder(NoOpPasswordEncoder.getInstance());

    }

    /**
     * 配置客户端详情。客户详细信息可以初始化，或者可以引用现有的store。
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // 使用内存存储客户端信息
                .withClient("ssoclient")
                .secret("ssosecret")
                .autoApprove(true)
                //OAuth2支持的验证模式
                .authorizedGrantTypes("refresh_token", "password", "authorization_code")
                .redirectUris("http://localhost:8081/login","http://localhost:8080/login")
                // 允许的授权范围
                .scopes("all")
                // 配置token的过期时间为1h
                .accessTokenValiditySeconds(3600 * 1000);
    }

    /**
     * 配置授权服务器端点，如令牌存储，令牌自定义，用户批准和授权类型，不包括端点安全配置
     * <p>
     * pathMapping 方法: 端点的默认（框架实现）URL路径，所需的自定义路径（以“/”开头）
     * 框架提供的URL路径是/oauth/authorize（授权端点），/oauth/token（令牌端点），
     * /oauth/confirm_access（用户在此处发布授权批准），/oauth/error（用于在授权服务器中呈现错误），
     * /oauth/check_token（由资源服务器用于解码访问令牌） ，和/oauth/token_key（如果使用JWT令牌，则公开用于令牌验证的公钥）。
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.accessTokenConverter(jwtAccessTokenConverter())
                // 配置安全认证管理
                .authenticationManager(authenticationManager);
    }

    /**
     * 查看证书信息命令：keytool -list -v -keystore 证书地址 -storepass 密码
     * 注意：出现异常信息时： Invalid keystore format
     * maven将resources下文件拷到classes下的时候导致的，当filtering为true时，
     * maven会在拷贝文件过程中进行变量替换，这导致原文件格式发生变化。
     * 而对于jks、so等二进制文件，文件内任意的字节变化都有可能导致文件不符合格式规范，
     * 所以会出现报错情况。
     *
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "tc123456".toCharArray())
                .getKeyPair("tycoonclient");
        converter.setKeyPair(keyPair);
        return converter;
    }
}
